
/***************************************************************************
 *   boque 
 *                                                                         *
 *   Copyright (C) 2010 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/


#include <iostream>
#include <string>
#include <pthread.h>
#include <signal.h>
#include <dlfcn.h>

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>

#include <boost/program_options.hpp>
namespace po = boost::program_options;

#include <boost/date_time/posix_time/posix_time.hpp>

#include "bocat/common/logging.hpp"
#include "bocat/common/time.hpp"

#include "handler.hpp"
#include "server.hpp"

static	bocat::common::Logger*	logger_p = NULL;

#define INFO(ARGS...)   logger_p->info(__LINE__, __FILE__, ARGS)
#define ERROR(ARGS...)  logger_p->error(__LINE__, __FILE__, ARGS)



int main(int argc, char* argv[])
{
    std::string ip;
    std::string port;
    int thread_count = 0;

	std::string	handler_so;
	std::string	handler_p1;
	std::string	handler_p2;

	std::string	log_config;
	std::string	log_section;


    try {
        po::options_description desc("Allowed options");
        desc.add_options()
            ("help,h", "produce help message")
            ("ip", po::value<std::string>(&ip)->default_value("0.0.0.0"), "server ip/hostname")
            ("port", po::value<std::string>(&port)->default_value("5555"), "server port")
            ("threads", po::value<int>(&thread_count)->default_value(1), "number of threads to run")

            ("handler-so", po::value<std::string>(&handler_so), "path to handler shared-object(.so) file")
            ("handler-p1", po::value<std::string>(&handler_p1), "p1 parameter of handler create(p1, p2)")
            ("handler-p2", po::value<std::string>(&handler_p2), "p2 parameter of handler create(p1, p2)")

            ("log-config", po::value<std::string>(&log_config), "path to logging config file")
            ("log-section", po::value<std::string>(&log_section), "logging section name in config file")
        ;

        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);

        if (vm.count("help")) {
            std::cout << desc << "\n";
            return 1;
        }

		if (vm.count("handler-so") < 1) {
            std::cout << "\nhandler-so must be provided\n\n";
            std::cout << desc << "\n";
            return 3;
		}

        if ((vm.count("log-config") > 0) && (vm.count("log-section") < 1)) {
            std::cout << "\nlog-section must be provided\n\n";
            std::cout << desc << "\n";
            return 3;
        }   
    }
    catch(std::exception& e) {
        std::cerr << "error: " << e.what() << "\n";
        return 1;
    }
    catch(...) {
        std::cerr << "Exception of unknown type!\n";
        return 2;
    }


	logger_p = bocat::common::getLogger();
    if ( log_config.size() > 0 ) { 
        logger_p->configure( log_config.c_str(), log_section.c_str() );
    }   

	try
	{
		// Block all signals for background thread.
		sigset_t new_mask;
		sigfillset(&new_mask);
		sigset_t old_mask;
		pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);

		INFO(" * initializing ...");

		// initialized Handler 
		boque::trasit::Handler*	handler_p = NULL;

		void*	handler_so_p = dlopen( handler_so.c_str(), RTLD_NOW );
		if ( NULL == handler_so_p ) {
			ERROR( "fail to dlopen %s -- %s", handler_so.c_str(), dlerror() );
			return	41;
		}

		const char*	dlsym_error = NULL;

		// reset errors
		dlerror();

		create_t*	create_handler = (create_t *)dlsym( handler_so_p, "create" );
		dlsym_error = dlerror();
		if ( NULL != dlsym_error ) {
			if ( NULL != handler_so_p ) {
				dlclose( handler_so_p );
				handler_so_p = NULL;
			}

			ERROR( "fail to dlsym 'create' from %s -- %s", handler_so.c_str(), dlsym_error );
			return	43;
		}

		// reset errors
		dlerror();

		destroy_t*	destroy_handler = (destroy_t *)dlsym( handler_so_p, "destroy" );
		dlsym_error = dlerror();
		if ( NULL != dlsym_error ) {
			if ( NULL != handler_so_p ) {
				dlclose( handler_so_p );
				handler_so_p = NULL;
			}

			ERROR( "fail to dlsym 'destroy' from %s -- %s", handler_so.c_str(), dlsym_error );
			return	43;
		}

		try {
			handler_p = create_handler( handler_p1.c_str(), handler_p2.c_str() );
		}
		catch( std::exception& e ) {
			if ( NULL != handler_so_p ) {
				dlclose( handler_so_p );
				handler_so_p = NULL;
			}

			ERROR( "fail to create Handler from %s -- %s", handler_so.c_str(), e.what() );
			return	45;
		}
		if ( NULL == handler_p ) {
			if ( NULL != handler_so_p ) {
				dlclose( handler_so_p );
				handler_so_p = NULL;
			}

			ERROR( "fail to create Handler from %s -- %s", handler_so.c_str(), dlsym_error );
			return	45;
		}

		INFO(" * listening on %s:%s ...", ip.c_str(), port.c_str());
		// Run server in background thread.
		////std::size_t num_threads = boost::lexical_cast<std::size_t>(argv[3]);
		boost::mutex	handler_mutex;
		boque::trasit::Server s(ip, port, thread_count, handler_p, &handler_mutex);
		boost::thread t(boost::bind(&boque::trasit::Server::run, &s));


		// Restore previous signals.
		pthread_sigmask(SIG_SETMASK, &old_mask, 0);

		// Wait for signal indicating time to shut down.
		sigset_t wait_mask;
		sigemptyset(&wait_mask);
		sigaddset(&wait_mask, SIGINT);
		sigaddset(&wait_mask, SIGQUIT);
		sigaddset(&wait_mask, SIGTERM);
		pthread_sigmask(SIG_BLOCK, &wait_mask, 0);
		int sig = 0;
		sigwait(&wait_mask, &sig);

		// Stop the server.
		s.stop();
		t.join();

		INFO("    shutdown ...");

		destroy_handler( handler_p );
		handler_p = NULL;

		dlclose( handler_so_p );
		handler_so_p = NULL;
	}
	catch (std::exception& e)
	{
		ERROR("exception: %s", e.what());
	}

	INFO("    done.");

	return 0;
}


